<?php
namespace Modules\Admin\Model\Service\Access;

use Modules\Admin\Model\Data\Menu as DataMenu;
use Modules\Admin\Model\Data\User as DataUser;
use Modules\Admin\Model\Data\Acl as DataAcl;
use Modules\Admin\Model\Util\Scan as UtilScan;
use S\Request;

class Control {

    /**
     * 获取后台菜单
     * @return array
     */
    public static function getNavMenu() {
        $data_menu = new DataMenu();
        $menu_controller_list = $data_menu->getMenuControlList();

        //非管理员用户去除权限之外的菜单
        if (!self::isAdmin()) {
            $acl = self::getUserAcl();
            foreach ($menu_controller_list as $key => &$menu_controller) {
                $menu_controller['controller'] = strtolower($menu_controller['controller']);
                $menu_controller['action'] = strtolower($menu_controller['action']);
                if (!isset($acl[$menu_controller['controller']]['methods'][$menu_controller['action']])) {
                    unset($menu_controller_list[$key]);
                }
            }
        }

        //生成二级菜单栏
        $nav = $leftNav = array();
        foreach ($menu_controller_list as $menu_controller) {
            $nav[$menu_controller['mid']][] = array(
                'name' => $menu_controller['uname'],
                'link' => APP_ADMIN_PATH . '/' . substr(strtolower($menu_controller['controller']), 11) . '/' . substr(strtolower($menu_controller['action']), 0, -6)
            );
        }

        //二级菜单栏分组
        $menu_list = $data_menu->getLeftNav();
        foreach ($menu_list as $key => $menu) {
            if (isset($nav[$key])) {
                $leftNav[$menu] = $nav[$key];
            }
        }
        return $leftNav;
    }

    /**
     * 用户是否可以访问当前地址
     * @param string $controller
     * @param string $action
     * @return bool
     */
    public static function isAccess($controller, $action = null) {
        $acl = self::getUserAcl();
        $controller = strtolower(Config::$class_prefix . $controller);
        $action = strtolower($action . Config::$action_suffix);
        return $action === null ? isset($acl[$controller]['methods']['indexaction']) : isset($acl[$controller]['methods'][$action]);
    }

    /**
     * 检查当前用户是否是管理员
     * @return bool
     */
    public static function isAdmin() {
        return Request::session('isadmin') ? true : false;
    }

    /**
     * 获取当前用户的acl
     * @return mixed
     */
    public static function getUserAcl() {
        return Request::session('actionlist');
    }

    /**
     * 返回用户是否可以设置ACL，管理员和被冻结用户不能设置
     * @param int|array $verify_data
     * @return bool
     */
    public static function isSetAcl($verify_data) {
        $data = array();
        if (is_numeric($verify_data)) {
            $data = (new DataUser())->getUserInfoById($verify_data);
        } elseif (is_array($verify_data)) {
            $data = $verify_data;
        }
        return $data['isadmin'] == '1' || $data['status'] != '0';
    }

    /**
     * 获取用户可访问的acl列表
     * @param int $uid
     * @return array
     */
    public static function accessibleAclData($uid) {
        $user_info = (new DataUser())->getUserInfoById($uid);

        //被封用户
        if ($user_info['status'] != '0') {
            return array();
        }

        //controller - action 列表
        $all = self::resetMethods(self::getList());

        //管理员
        if ($user_info['isadmin'] == '1') {
            return $all;
        }

        //普通用户
        $acl = self::aclData(Config::ACL_UID, $uid);

        //只保留allow
        $data = array();
        foreach ($acl as $key => $value) {
            $key = strtolower($key);
            $action_list = array();
            if (empty($value['action'])) {
                continue;
            }

            foreach ($value['action'] as $action => $option) {
                $action = strtolower($action);
                //allow
                if (isset($all[$key]['methods'][$action]) && $option == 'allow') {
                    $action_list[$action] = $all[$key]['methods'][$action];
                }
            }

            //如果有可以访问的action，则写入数据
            if (!empty ($action_list)) {
                $data[$key] = $all[$key];
                $data[$key]['methods'] = $action_list;
            }
        }
        return $data;
    }

    /**
     * 转换执行方法格式小写方法名+Action
     *
     * @param array $all
     * @return array
     */
    private static function resetMethods($all) {
        $lower = array();
        foreach ($all as $key => $value) {
            $methods = array();
            foreach ($value['methods'] as $action_key => $action_value) {
                $methods[strtolower($action_key)] = $action_value;
            }
            $lower[strtolower($key)] = array('name' => $value['name'], 'methods' => $methods);
        }
        unset($all, $key, $value);
        return $lower;
    }

    /**
     * 获取用户|用户组|全局acl
     *
     * @param string  $type uid|gid|all
     * @param int     $id
     * @param boolean $kv
     * @return array
     */
    public static function aclData($type, $id, $kv = false) {
        //	获取全局的配置
        $data = (array)self::getAcl('all', 0, $kv);

        //	获取用户组的配置
        if ($type === Config::ACL_GID) {
            $group = self::getAcl($type, $id, $kv);
            //合并
            $data = self::arrayMergeRecursiveDistinct($data, $group);
        }

        //	获取用户的配置
        if ($type === Config::ACL_UID) {
            $groups = self::getAcl(Config::ACL_UGROUP, $id, $kv);
            $user = self::getAcl($type, $id, $kv);
            $data = self::arrayMergeRecursiveDistinct($data, (array)$groups, (array)$user);
        }
        return $data;
    }

    /**
     * 获取acl表
     *
     * @param string  $type
     * @param int     $id
     * @param boolean $kv 以controller-action = option的形式返回
     *
     * @return array
     */
    private static function getAcl($type, $id, $kv = false) {
        $data_acl = new DataAcl();
        if ($type === Config::ACL_UID) {//uid
            $data = $data_acl->getUserAclList($id);
        } else if ($type === Config::ACL_GID) {//gid
            $data = $data_acl->getGroupAclList($id);
        } else if ($type === Config::ACL_UGROUP) {//user all groups
            $data = $data_acl->getUserGroupAclList($id);
            return self::toAclData($data, $kv, true);
        } else {//all
            $data = $data_acl->getAllAclList();
        }

        return self::toAclData($data, $kv);
    }

    /**
     * 转换到acl数组
     *
     * @param array $data
     * @param bool  $kv 以kay-value格式返回数据
     * @param bool  $override 出现重复是否覆盖（主要用于处理用户组权限冲突）
     * @return array
     *    array(
     *        controller => array(
     *            'option' => 'allow',
     *            'action' => array(
     *                action => 'deny',
     *            )
     *        ),
     *    )
     *    [controller][option] = 'option'
     *    [controller]['action'][action] = 'option'
     */
    private static function toAclData($data, $kv = false, $override = false) {
        $new = array();
        if (!empty($data)) {
            foreach ($data as $v) {
                //操作权限
                $option = $v['option'] ? $v['option'] : 'allow';
                if ($kv === false) {
                    if (!empty($v['action'])) {
                        //	allow 覆盖 deny（如果一个用户属于几个不同的用户组，只要一个组允许就允许，即使其他组设置为禁止）
                        if (!($override && isset($new[$v['controller']]['action'][$v['action']]) && $option !== 'allow')) {
                            $new[$v['controller']]['action'][$v['action']] = $option;
                        }
                    } else {
                        if (!($override && isset($new[$v['controller']]) && $option !== 'allow')) {
                            $new[$v['controller']]['option'] = $option;
                        }
                    }
                } else {
                    if (!empty($v['action'])) {
                        if (!($override && isset($new[$v['controller'] . '-' . $v['action']]) && $option !== 'allow')) {
                            $new[$v['controller'] . '-' . $v['action']] = $option;
                        }
                    } else {
                        if (!($override && isset($new[$v['controller']]) && $option !== 'allow')) {
                            $new[$v['controller']] = $option;
                        }
                    }
                }//end if

            }//end foreach
        }
        return $new;
    }

    /**
     * @return array
     */
    public static function getList() {
        $dir = APP_PATH . "/modules/Admin/controllers";
        return UtilScan::classes($dir, Config::getActionRules());
    }

    /**
     * 递归地合并一个或多个数组
     * 来自php手册array_merge_recursive的评论部分
     *
     * @return array
     */
    private static function arrayMergeRecursiveDistinct() {
        $arrays = func_get_args();
        $base = array_shift($arrays);
        if (!is_array($base)) {
            $base = empty($base) ? array() : array($base);
        }
        foreach ($arrays as $append) {
            if (!is_array($append))
                $append = array($append);
            foreach ($append as $key => $value) {
                if (!array_key_exists($key, $base) && !is_int($key)) {
                    $base[$key] = $append[$key];
                    continue;
                }
                if (is_array($value) or is_array($base[$key])) {
                    $base[$key] = self::arrayMergeRecursiveDistinct($base[$key], $append[$key]);
                } else if (is_numeric($key)) {
                    if (!in_array($value, $base))
                        $base[] = $value;
                } else {
                    $base[$key] = $value;
                }
            }
        }
        return $base;
    }
}
